/********************************************************************************
 *
 * jMule - a Java massive parallel file sharing client
 *
 * Copyright (C) by the jMuleGroup ( see the CREDITS file )
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details ( see the LICENSE file ).
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * $Id: Convert.java,v 1.27 2003/12/06 18:14:22 lydna Exp $
 *
 ********************************************************************************/
package ants.p2p.utils.donkey;

import java.nio.*;
import java.util.*;
import java.util.regex.*;

/** Provides  various conversion ( e.g. str <-> hex ) utility methods.
 *
 * @version $Revision: 1.27 $
 * <br>Last changed by $Author: lydna $ on $Date: 2003/12/06 18:14:22 $
 */
public class Convert {

    /** converts a textual representation of an InternetProtocol (IP ) ver. 4 address to an array of bytes.
     * @return array of bytes like '1.2.3.4' -> return_bytes[3] = 1; return_bytes[2] = 2; return_bytes[1] = 3; return_bytes[0] = 4
     */
    public final static byte[] ipStringToBytes(String ipString) {
        Pattern ipRegex = Pattern.compile("\\A(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\z");
        Matcher ipMatcher = ipRegex.matcher(ipString);
        if (!ipMatcher.matches())
            return null;
        byte[] result = new byte[4];
        for (int i = 0; i < 4; i++) {
            String tmp = ipMatcher.group(i + 1);
            Integer itmp = new Integer(tmp);
            result[i] = intToByte(itmp.intValue());
        }
        return result;
    }

    /** converts a hexString to the byte represenation,*/
    public final static byte[] hexStringToBytes(String hexString) {
        byte[] bytes = new byte[hexString.length() / 2];
        for (int i = 0; i < bytes.length; i++) {
            bytes[i] = hexToByte(hexString, i * 2);
        }
        return bytes;
    }

    final static char[] hexChars = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
    /** convience method to append a byte b to the given StringBuffer sb. */
    public static final StringBuffer appendHexDigit(StringBuffer sb, byte b) {
        sb.append(hexChars[(b & 0xF0) >>> 4]);
        sb.append(hexChars[(b & 0x0F)]);

        return sb;
    }

    /** appends a byte array values to a StringBuffer as hex digits. The hexdigist are 2 chars in length. */
    public static final void appendAsHex(StringBuffer sb, byte[] data) {
        for (int c = 0; c < data.length; c++) {
            appendHexDigit(sb, data[c]);
        }
    }

    /** appends a byte array values to a StringBuffer as hex digits. The hexdigist are 2 chars in length. */
    public static final void appendAsHex(StringBuffer sb, Byte[] data) {
        for (int c = 0; c < data.length; c++) {
            appendHexDigit(sb, data[c].byteValue());
        }
    }

    /** appends a ByteBuffers values to a StringBuffer as hex digits. The hexdigist are 2 chars in length.
         * begins at ByteBuffer.position(), appends to the StringBuffer until limit() and then sets position()
         * back to it's start value.
         */
    public static final void appendAsHex(StringBuffer sb, ByteBuffer data) {
        int pos = data.position();
        while (data.remaining() > 0) {
            appendHexDigit(sb, data.get());
        };
        data.position(pos);
    }

    /** converts the byte b to its String representation. */
    public static final String byteToHex(byte b) {
        return appendHexDigit(new StringBuffer(), b).toString();
    }

    /** converts a byte array values to a String of hex digits. */
    public static final String bytesToHexString(byte[] data) {
        StringBuffer sb = new StringBuffer(data.length * 2);
        appendAsHex(sb, data);
        return sb.toString();
    }

    /** converts a Byte array values to a String of hex digits. */
    public static final String bytesToHexString(Byte[] data) {
        StringBuffer sb = new StringBuffer(data.length * 2);
        appendAsHex(sb, data);
        return sb.toString();
    }

    /** converts a ByteBuffer values to a String of hex digits.
         * we read the buffer from the current position to the end. After reading we set the old position() back.
         * this version behaves just like bytesToHexString( data, data.position(); data.limit() )
         */
    public final static String byteBufferToHexString(ByteBuffer data) {
        return byteBufferToHexString(data, data.position(), data.limit());
    }

    /** converts a ByteBuffer values to a String of hex digits.
         * we read length bytes from the given offset. After reading we set the old position() back.
    */
    public final static String byteBufferToHexString(ByteBuffer data, int offset, int length) {
        if (offset >= data.capacity()) {
            return "";
        }
        length = length + offset > data.capacity() ? data.capacity() - offset : length;
        StringBuffer sb = new StringBuffer(length * 2);
        int pos = data.position();
        data.position(offset);
        int limit = data.limit();
        data.limit(offset + length);
        while (length > 0 && data.hasRemaining()) {
            appendHexDigit(sb, data.get());
            length--;
        };
        data.limit(limit);
        data.position(pos);
        return sb.toString();
    }

    /**
    * converts a byte array to hex digits and pretty formats it.
    * The data will be splitted into chunks of 10 bytes each line.
    */
    /*
    public static final String toPrettyHexString( byte[] data ) {
      StringBuffer sb = new StringBuffer( data.length * 2 );
      appendAsHex( sb, data );
      return StringBufferFormater.format( sb, 10, '\n'  ).toString();
    }
    */

    /** converts a hex String (like "FF0B") to byte[].
     * and whats does Byte.decode(String) ?
         */

    public final static byte hexToByte(String hex, int pos) {

        int erg = 0;
        hex = hex.toUpperCase();
        for (int j = 0; j < 2; j++) {
            for (int i = 0; i < 16; i++) {
                if (hex.charAt(pos + 1 - j) == hexChars[i]) {
                    erg += i * (int)Math.pow(16, j);
                }
            }
        }
        return intToByte(erg);
    }

    /** Interprets a string as boolean value.
         * If the given @param str is not null and equals to 't(rue)' 'y(es)' ( or a localized equivalent) or '1', it will be treated as true, else false.
         */
    public final static boolean stringToBoolean(String str) {

        return (
            str.equalsIgnoreCase("true")
                || str.toLowerCase().startsWith("true")
                || str.equalsIgnoreCase("yes")
                || str.toLowerCase().startsWith("yes")
                || str.equals("1"))
            ? true
            : false;
    }

    public final static BitSet bytesToBitset(byte[] buffer, int pos, int length) {
        int nbits = length * 8;
        BitSet result = new BitSet(nbits);
        if (pos + length > buffer.length) {
            System.out.println("ERROR in bytesToBitset."); // FIXME: ugly, do some logging...
            return result;
        }
        for (int i = 0; i < length; i++) {
            byte b = buffer[i + pos];
            for (int j = 0; j < 8; j++) {
                if ((b & 1 << j) != 0) {
                    //"byte" * 8 + "bit in byte" = i * 8 + j
                    result.set((i << 3) + j);
                }
            }
        }
        return result;
    }

    public final static void bitSetToBytes(BitSet bitSet, byte[] bytes, int pos) {
        for (int i = 0; i < bitSet.length(); i++)
            if (bitSet.get(i))
                bytes[(i / 8) + pos] |= 1 << (i % 8);
    }

    /**
     * @param bitSet BitSet to convert
     * @param realsize maximum used bits in set possibly more than bitSet.length() and less than bitSet.size().
     * @return a byte[] k with k.length() == (realsize + 7) / 8
     */
    public final static byte[] bitSetToBytes(BitSet bitSet, int realsize) {
        byte[] bytes = null;
        if (bitSet.length() <= realsize) {
            bytes = new byte[(realsize + 7) / 8];
            bitSetToBytes(bitSet, bytes, 0);
        } else {
            bytes = new byte[(bitSet.size() + 7) / 8];
            bitSetToBytes(bitSet, bytes, 0);
        }
        return bytes;
    }

    /** converts the myByte to a int. */
    public final static int byteToInt(byte myByte) {
        return (int) (myByte & 0xff);
    }

    /** converts the given int value to byte. */
    public final static byte intToByte(int value) {
        return (byte) (value & 0xff);
    }

    /** converts an array of ints to an array of bytes using the Convert.intToByte() method.  */
    public final static byte[] intArrayToBytes(int[] iarray) {
        byte[] buffer = new byte[iarray.length];
        for (int i = 0; i < iarray.length; i++) {
            buffer[i] = intToByte(iarray[i]);
        }
        return buffer;
    }

    /** coverts an array of unsigned bytes to an array of integers using the Convert.byteToInt(). */
    public final static int[] unsingedByteArrayToInts(byte[] barray) {
        int[] buffer = new int[barray.length];
        for (int i = 0; i < barray.length; i++) {
            buffer[i] = byteToInt(barray[i]);
        }
        return buffer;
    }

    /** converts the array of integers to an InternetProtocol ver. 4 address in the form "iarray[0].iarray[1].iarray[2].iarray[3]"
     * @param iarray must be of the length = 4; all other entrys are ignored.
     */
    public final static String intArrayToIpString(int[] iarray) {
        String ip = "";
        //return (null);
        for (int i = 0; i < 4; i++) {
            ip += Integer.toString(iarray[i]);
            if (i != 3)
                ip += ".";
        }
        return ip;
    }

    public final static String byteArrayToIpString(byte[] barray) {
        int[] iarray = unsingedByteArrayToInts(barray);

        return intArrayToIpString(iarray);

    }

    /** Interprets a byte as unsigned and cast it to long.
     * @param b the unsigned byte as java byte.
     * @return a non-negative long k with (byte)k==b.
     */
    public final static long unsingedByteToLong(byte b) {
        return b < 0 ? ((long)b) & 0xFFL : b;
    }

    public final static long unsignedIntToLong(int i) {
        return i < 0 ? ((long)i) & 0xFFFFFFFFL : i;
    }

    /** converts a unsigned short into integer.
     */
    public final static int unsignedShortToInt(short i) {
        return (i < 0 ? ((int)i) & 0xFFFF : i);
    }

    public final static Locale parseLocale(String localeAsStr) {
        /*
        Locale locale = null;

        String lang = "";
        String country = "";
        String variant = "";

        java.util.StringTokenizer st = new java.util.StringTokenizer(localeAsStr, "_");
        try {

            lang = st.nextToken();
            country = st.nextToken();
            variant = st.nextToken();
        } catch (java.util.NoSuchElementException nse_err) {
        }
*/
        return new Locale(localeAsStr /*lang, country, variant*/
        );
    }
}
